
#include "test_case.hpp"

/*
g++ test_case.cpp -static -I../insdir/include -L../insdir/lib -lboost_system -lpthread -lboost_coroutine -lboost_context
*/

int test_case0_p11_boost_version(void) {
    using namespace std;

    cout << BOOST_VERSION << endl;
    cout << BOOST_LIB_VERSION << endl;
    cout << BOOST_PLATFORM << endl;
    cout << BOOST_COMPILER << endl;
    //cout << BOOST_STDLIB << endl;
    return 0;
}


/*
一旦另一个程序建立连接，accept_handler()被调用。如果这个连接成功建立，就用boost::asio::async_write()发送当前时间，
这个函数在socket上发送所有data中的数据。boost::asio::ip::tcp::socket也提供了另一个成员函数async_write_some()，该函数在至少发送一个字节后调用handler句柄函数，
因此，句柄函数必须检查多少字节已被发，还剩多少要发，再一次说明，句柄函数要调用async_write_some()。
使用boost::asio::async_write()能避免采用不断地判断待发的字节数和调用async_write_some()这种方式，这个异步操作仅当所有的字节都发送后才算完成。

所有字节都发送后，write_handler()被调用。这个函数以boost::asio::ip::tcp::socket::shutdown_send为参数调用shudown()，
表明程序在这个sokect上已发送完数据。由于没有挂起的异步操作，示例32.6的程序就退出了。请注意，尽管变量data仅被用在accept_handler()函数中，它不能是一个局部变量。
当boost::asio::async_write()和 accept_handler()返回时，异步操作就开始了，但并没有结束，data必须存在，直到异步操作完成为止，如果data是一个全局变量，就可以做到这点。
*/


// io_service ioservice;
// tcp::endpoint tcp_endpoint{tcp::v4(), 2014};
// tcp::acceptor tcp_acceptor{ioservice, tcp_endpoint};
// tcp::socket tcp_socket{ioservice};
// std::string data;
 
// void write_handler(const boost::system::error_code &ec,
//   std::size_t bytes_transferred)
// {
//   if (!ec)
//     tcp_socket.shutdown(tcp::socket::shutdown_send);
// }
 
// void accept_handler(const boost::system::error_code &ec)
// {
//   if (!ec)
//   {
//     std::time_t now = std::time(nullptr);
//     data = std::ctime(&now);
//     async_write(tcp_socket, buffer(data), write_handler);
//   }
// }


// void test_case32_6() {
//     tcp_acceptor.listen();
//     tcp_acceptor.async_accept(tcp_socket, accept_handler);
//     ioservice.run();
// }

/*

用Boost.Asio使用协程的函数调用是boost::asio::spawn()。第一个传递的参数必须是I/O服务对象，第二个参数是协程函数，这个函数必须接受一个boost::asio::yield_context类型的参数作为其唯一参数，
且不能有返回值。示例32.7中，do_accept()和do_write()作为协程，如果函数类型不同，就像do_write()一样，你必须用std::bind或lambda函数来做一个适配器。

与句柄不同，你可以传递一个boost::asio::yield_context类型的对象给异步函数。do_accept()传递参数yield给async_accept()，在do_write()中，
yield传递给async_write()。这些函数调用仍然开启异步操作，但操作完成后没有句柄函数被调用，而是将异步操作开始的上下文保存起来，当异步操作完成时，程序从它离开的地方继续运行。

do_accept()包含一个循环，当这个函数被每次被调用时，一个新的socket传递给async_accept()。一旦一个客户端建立了一个连接，用boost::asio::spawn()
将do_write()作为协程，do_write()被调用，将当前时间发送给客户端。

很容易看出，for循环使程序在接受两个客户端后就退出了。由于这个示例基于协程，不断执行异步操作可以通过for循环来实现。
这样提高了程序的可读性，因为你不需要关注当上一个异步操作完成后可能产生的句柄函数调用。如果这个时间服务器需要支持超过两个客户端，仅修改for循环就可以了。

*/

io_service ioservice;
tcp::endpoint tcp_endpoint{tcp::v4(), 2014};
tcp::acceptor tcp_acceptor{ioservice, tcp_endpoint};
std::list<tcp::socket> tcp_sockets;
 
void do_write(tcp::socket &tcp_socket, yield_context yield)
{
  std::time_t now = std::time(nullptr);
  std::string data = std::ctime(&now);
  async_write(tcp_socket, buffer(data), yield);
  tcp_socket.shutdown(tcp::socket::shutdown_send);
}
 
void do_accept(yield_context yield)
{
  for (int i = 0; i < 2; ++i)
  {
    tcp_sockets.emplace_back(ioservice);
    tcp_acceptor.async_accept(tcp_sockets.back(), yield);
    spawn(ioservice, [](yield_context yield)
      { do_write(tcp_sockets.back(), yield); });
  }
}


void test_case32_7() {
    tcp_acceptor.listen();
    spawn(ioservice, do_accept);
    ioservice.run();
}


int main() {

    test_case0_p11_boost_version();
    //test_case32_6();
    test_case32_7();
    return 0;
}   